/**
 * Libevent例程：回显服务器
 * @author Martin
 * @date 2021-8-10
 * @note 
 * 编译：
 * $ gcc -o echoserver Echo_Server.c -levent
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <event.h>
#include <string.h>
#include <event2/listener.h>

static void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
        struct sockaddr *address, int socklen, void *ctx);
static void echo_read_cb(struct bufferevent *bev,void *ctx);
static void echo_event_cb(struct bufferevent *bev,short events,void *ctx);

/**
 * 连接请求回调函数
 * @param listener 连接监听器
 * @param fd 连接文件描述符
 * @param address 连接源地址
 * @param socklen address长度
 * @param ctx 用户参数，对应evconnlistener_new_bind的ptr参数
 */
static void
accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
                           struct sockaddr *address, int socklen, void *ctx)
{
    struct event_base *base = evconnlistener_get_base(listener);

    /* bufferevent是封装成带缓冲区的socket套接字 */
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); /* 创建基于套接字的bufferevent */

    /* 设置bufferevent的3个回调函数readcb（可读时回调函数），writecb（可写时回调函数），errorcb（错误发生时回调函数） */
    bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL); /* 修改buffevent回调 */
    /* 默认情况，新建的bufferevent时写入启用，读取未启用的 */
    bufferevent_enable(bev, EV_READ | EV_WRITE); /* 使能bufferevent写入，读取 */
}

/**
 * 连接错误处理回调函数
 * @param listener
 * @param ctx
 */
static void
accept_error_cb(struct evconnlistener *listener, void *ctx)
{
    if (listener) {
        perror("accept error");
        evconnlistener_free(listener);
    }
}

/**
 * this callback is invoked when there is data to read on bev
 */
static void echo_read_cb(struct bufferevent *bev,void *ctx)
{
    struct evbuffer *input = bufferevent_get_input(bev); /* 获取输入缓冲区 */
    struct evbuffer *output = bufferevent_get_output(bev); /* 获取输出缓冲区 */

    /* copy all data from the input buffer to the ouput buffer. */
    evbuffer_add_buffer(output, input); /* 将缓冲区input内容移动到缓冲区output*/
}

static void
echo_event_cb(struct bufferevent *bev,short events,void *ctx)
{
    if (events & BEV_EVENT_ERROR)
        perror("Error from bufferevent");
    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
        bufferevent_free(bev);
}

int main_echoserver(int argc, char *argv[])
{
    struct event_base *base;
    struct evconnlistener *listener;
    struct sockaddr_in sin;

    int port = 9876;
    if (argc > 1) {
        port = atoi(argv[1]);
    }
    if (port <= 0 || port > 65535) {
        fprintf(stderr, "invalid port\n");
        return 1;
    }

    base = event_base_new();
    if (!base) {
        fprintf(stderr, "Could not open event base\n");
        return 1;
    }

    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY; // htonl(0);
    sin.sin_port = htons(port);

    /* 创建连接监听器，绑定ip地址，建立监听队列 */
    listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
                                        LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
                                        (struct sockaddr *)&sin, sizeof(sin));
    if (!listener) {
        perror("Couldnot create listener");
        return 1;
    }

    /* 设置错误回调函数 */
    evconnlistener_set_error_cb(listener, accept_error_cb);

    event_base_dispatch(base);

    /* 释放资源 */
    evconnlistener_free(listener);
    event_base_free(base);
    return 0;
}
